package lib

/*dfa算法实现敏感词检测*/

type Dfa struct {
	root *Node
}

func NewDfa(words []string) *Dfa {
	_dfa := &Dfa{
		root: new(Node),
	}
	_dfa.Build(words)
	return _dfa
}

// Build 构建dfa结构
func (d *Dfa) Build(words []string) {
	for _, word := range words {
		d.root.AddWord(word)
	}
}

// 敏感词替换
func replaceRune(chars []rune, replaceChar rune, begin, end int) {
	for i := begin; i < end; i++ {
		chars[i] = replaceChar
	}
}

// Search 判断是否存在敏感词
func (d *Dfa) Search(text string) bool {
	if d.root == nil {
		return false
	}

	textChars := []rune(text)
	length := len(textChars)
	for i := 0; i < length; i++ {
		temp := d.root.FindChild(textChars[i])
		if temp == nil {
			continue
		}

		j := i + 1
		for ; j < length && temp != nil; j++ {
			if temp.End {
				return true
			}
			temp = temp.FindChild(textChars[j])
		}

		// 尾部命中
		if j == length && temp != nil && temp.End {
			return true
		}

	}

	return false
}

// FindAll 找出所有敏感词
func (d *Dfa) FindAll(text string) []string {
	if d.root == nil {
		return nil
	}

	textChars := []rune(text)
	textCharsCopy := make([]rune, len(textChars))
	copy(textCharsCopy, textChars)

	length := len(textChars)
	var sensitiveWords []string
	for i := 0; i < length; i++ {
		temp := d.root.FindChild(textChars[i])
		if temp == nil {
			continue
		}

		j := i + 1
		for ; j < length && temp != nil; j++ {
			if temp.End {
				sensitiveWords = append(sensitiveWords, string(textChars[i:j]))
			}
			temp = temp.FindChild(textChars[j])
		}

		// 尾部命中
		if j == length && temp != nil && temp.End {
			sensitiveWords = append(sensitiveWords, string(textChars[i:length]))
		}

	}

	return sensitiveWords
}

/* Node 节点 */

type Node struct {
	End bool // 是否最后一位
	//Next map[string]*Node // 节点
	Next map[rune]*Node // 节点
}

// AddChild 增加节点
func (n *Node) AddChild(c rune) *Node {
	if n.Next == nil {
		//n.Next = make(map[string]*Node)
		n.Next = make(map[rune]*Node)
	}

	// 节点存在
	if next, ok := n.Next[c]; ok {
		return next
	} else {
		n.Next[c] = new(Node)
		return n.Next[c]
	}
}

// FindChild 查找节点
func (n *Node) FindChild(c rune) *Node {
	if n.Next == nil {
		return nil
	}

	//_c := string(c)
	_c := c
	if _, ok := n.Next[_c]; ok {
		return n.Next[_c]
	}

	return nil
}

// AddWord 序列化字
func (n *Node) AddWord(word string) {
	node := n
	chars := []rune(word)
	for idx, _ := range chars {
		//node = node.AddChild(string(chars[idx]))
		node = node.AddChild(chars[idx])
	}
	node.End = true
}
